package com.chd.ledger_server.aspect;

import com.chd.ledger_server.dao.ModelBasePointsDao;
import com.chd.ledger_server.domain.PageDto;
import com.chd.ledger_server.entity.DevDevices;
import com.chd.ledger_server.entity.ModelBaseModels;
import com.chd.ledger_server.entity.ModelBasePoints;
import com.chd.ledger_server.entity.ModelInstanceModels;
import com.chd.ledger_server.entity.ModelInstancePoints;
import com.chd.ledger_server.service.DevDevicesService;
import com.chd.ledger_server.service.ModelBaseModelsService;
import com.chd.ledger_server.service.ModelBasePointsService;
import com.chd.ledger_server.service.ModelInstancePointsService;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 实模插入切面类
 * 处理实模(ModelInstanceModels)插入成功后自动联插设备(DevDevices)和实点(ModelInstancePoints)的逻辑
 * 采用AOP面向切面编程思想，将联插逻辑与主业务逻辑解耦，便于维护和扩展
 * <p>
 * 基点编码规则调整说明：
 * 原规则：[类型]_[数字1]_[数字2]_[数字3]
 * 新规则：[类型]_[设备类型]_[数字1]_[数字2]_[数字3]（第二个字段为设备类型，原数字字段顺次后移）
 */
@Aspect
@Component
@Slf4j
public class ModelInstanceInsertAspect {

    // 注入设备服务，用于设备数据的插入操作
    @Resource
    private DevDevicesService devDevicesService;

    // 注入实点服务，用于实点数据的插入操作
    @Resource
    private ModelInstancePointsService modelInstancePointsService;

    // 注入基模服务，用于查询基模(ModelBaseModels)相关信息
    @Resource
    private ModelBaseModelsService modelBaseModelsService;

    // 注入基点服务，用于查询基点(ModelBasePoints)相关信息
    @Resource
    private ModelBasePointsService modelBasePointsService;
    @Resource
    private ModelBasePointsDao modelBasePointsDao;

    /**
     * 切入点定义：匹配实模服务实现类的insert方法
     * 仅当方法参数为ModelInstanceModels类型时触发
     */
    @Pointcut("execution(* com.chd.ledger_server.service.impl.ModelInstanceModelsServiceImpl.insert(..)) && args(modelInstanceModels)")
    public void instanceInsertPointcut(ModelInstanceModels modelInstanceModels) {
    }

    /**
     * 后置通知：实模插入成功后执行设备和实点的联插逻辑
     *
     * @param modelInstanceModels 插入的实模对象
     * @param result              实模插入方法的返回结果（插入成功的实模对象）
     */
    @AfterReturning(value = "instanceInsertPointcut(modelInstanceModels)", returning = "result")
    public void afterInstanceInsert(ModelInstanceModels modelInstanceModels, Object result) {
        log.info("开始处理实模[{}]的设备和实点联插", modelInstanceModels.getInstanceName());

        try {
            // 1. 解析实模名称，提取层数、管排编号、基模名称等关键信息
            InstanceNameInfo nameInfo = parseInstanceName(modelInstanceModels.getInstanceName());

            // 2. 查询实模关联的基模信息（基模数据已预先插入）
            ModelBaseModels baseModel = modelBaseModelsService.queryById(modelInstanceModels.getBaseId());
            if (baseModel == null) {
                log.error("基模不存在，基模ID：{}，无法联插数据", modelInstanceModels.getBaseId());
                return;
            }
            nameInfo.setBaseModelKey(getBaseModelKey(baseModel.getModelName()));

            // 3. 查询该基模下的所有基点（基点数据已预先插入）
            ModelBasePoints queryPoint = new ModelBasePoints();
            queryPoint.setBaseId(baseModel.getBaseId());
            // 分页查询基点数据（默认查询全量）
            List<ModelBasePoints> basePoints = modelBasePointsDao.queryAll(queryPoint);

            // 4. 遍历基点，批量生成并插入设备和实点数据
            for (ModelBasePoints basePoint : basePoints) {
                // 4.1 生成设备数据并插入数据库
                DevDevices device = generateDevice(basePoint, modelInstanceModels, nameInfo);
                DevDevices savedDevice = devDevicesService.insert(device);

                // 4.2 生成实点数据并插入数据库（关联实模、基点和设备）
                ModelInstancePoints instancePoint = generateInstancePoint(
                        modelInstanceModels, basePoint, savedDevice);
                modelInstancePointsService.insert(instancePoint);
            }

            log.info("实模[{}]联插完成，共生成{}条设备和实点数据",
                    modelInstanceModels.getInstanceName(), basePoints.size());
        } catch (Exception e) {
            log.error("实模联插设备和实点数据失败", e);
            // 抛出运行时异常，触发事务回滚（需确保外层方法开启事务）
            throw new RuntimeException("联插设备和实点数据失败", e);
        }
    }

    /**
     * 获取基模关键字（用于判断基模类型）
     *
     * @param baseModelName 基模名称
     * @return 基模关键字（如HSX、ZZ等）
     */
    private String getBaseModelKey(String baseModelName) {
        // 映射基模名称到关键字（可根据实际业务补充完整）
        Map<String, String> modelKeyMap = new HashMap<>();
        modelKeyMap.put("后水悬吊管", "HSX");
        modelKeyMap.put("炉膛水冷壁（前）", "SLBQH");
        modelKeyMap.put("炉膛水冷壁（侧）", "SLBLC");
        modelKeyMap.put("炉膛水冷壁（斜坡）", "SLBXP");
        modelKeyMap.put("水冷壁拉稀管", "SLBLX");
        modelKeyMap.put("后竖井前后包墙", "HSJBQH");
        modelKeyMap.put("后竖井侧包墙", "HSJBQLC");
        modelKeyMap.put("水平烟道两侧包墙", "SPYDBQLC");
        modelKeyMap.put("壁式再热器（前）", "BZQ");
        modelKeyMap.put("壁式再热器（侧）", "BZLC");
        modelKeyMap.put("全大屏过热器", "QDP");
        modelKeyMap.put("后屏过热器", "HP");
        modelKeyMap.put("中温再热器", "ZZ");
        modelKeyMap.put("高温再热器", "GZ");
        modelKeyMap.put("高温过热器", "GG");
        modelKeyMap.put("立式低温过热器", "LSDG");
        modelKeyMap.put("省煤器悬吊管", "SMXXD");
        modelKeyMap.put("顶棚过热器", "DP");
        modelKeyMap.put("水平烟道上下包墙", "SPYDBQS");
        modelKeyMap.put("后竖井顶包墙", "HSJBQS");
        modelKeyMap.put("水平低温过热器", "SPDG");
        modelKeyMap.put("省煤器", "SMQ");
        return modelKeyMap.getOrDefault(baseModelName, "UNKNOWN");
    }

    /**
     * 解析实模名称，提取关键信息
     * 实模名称格式分为两种：
     * 1. 带#和·：#标识+·基模名称，例如#2层33·水平低过、#32·中温再热器
     * 2. 不带#和·：直接为基模名称，如后水悬吊管（单行单列类型）
     *
     * @param instanceName 实模名称
     * @return 解析结果封装对象
     */
    private InstanceNameInfo parseInstanceName(String instanceName) {
        InstanceNameInfo info = new InstanceNameInfo();

        // 处理不带#和·的实模名称（单行单列类型）
        if (!instanceName.contains("#") && !instanceName.contains("·")) {
            info.setBaseModelName(instanceName);
            info.setSingleRowOrColumn(true);
            return info;
        }

        // 处理带#和·的实模名称（多行多列类型）
        info.setSingleRowOrColumn(false);
        // 正则表达式：提取#和·之间的内容（标识部分）
        Pattern pattern = Pattern.compile("#(.*?)·");
        Matcher matcher = pattern.matcher(instanceName);

        if (matcher.find()) {
            String prefix = matcher.group(1);
            // 判断是否包含层结构（标识中含"层"字）
            info.setHasLayer(prefix.contains("层"));
            // 提取数字部分（去除"层"字后）
            String numberStr = prefix.replace("层", "");
            try {
                info.setExtractedNumber(Integer.parseInt(numberStr));
                // 若包含层结构，同步设置层数
                if (info.isHasLayer()) {
                    info.setLayerNum(info.getExtractedNumber());
                }
            } catch (NumberFormatException e) {
                log.warn("实模名称[{}]中的数字提取失败", instanceName, e);
            }
        }

        // 提取·后面的基模名称
        String[] nameParts = instanceName.split("·");
        if (nameParts.length > 1) {
            info.setBaseModelName(nameParts[1]);
        } else {
            log.warn("实模名称格式异常，无法提取基模名称：{}", instanceName);
            info.setBaseModelName("未知基模");
        }
        return info;
    }

    /**
     * 根据基点信息、实模信息生成设备数据
     *
     * @param basePoint     基点对象（包含基点名称等信息）
     * @param instanceModel 实模对象（包含实模ID等信息）
     * @param nameInfo      实模名称解析结果
     * @return 生成的设备对象
     */
    private DevDevices generateDevice(ModelBasePoints basePoint,
                                      ModelInstanceModels instanceModel,
                                      InstanceNameInfo nameInfo) {
        DevDevices device = new DevDevices();
        String basePointName = basePoint.getPointName(); // 基点名称，新格式：[类型]_[设备类型]_[数字1]_[数字2]_[数字3]

        // 1. 生成设备名称（结合基点名称和实模解析信息）
        String deviceName = generateDeviceName(basePointName, nameInfo);
        device.setDeviceName(deviceName);

        // 2. 生成设备位置描述（解析设备名称，转换为可读性文字）
        device.setDeviceDesc(generateDeviceDesc(deviceName));

        // 3. 设置设备其他默认属性
        device.setDeviceTypeId(1); // 设备类型ID默认值（可根据业务调整）
        device.setStatus(3); // 设备状态：3表示"投用"状态

        return device;
    }

    /**
     * 生成设备名称
     * 设备名称格式：[类型]_[设备类型]_[数字1]_[数字2]_[数字3]_[层数|null]
     * （因基点编码第二个字段变为设备类型，原数字字段顺次后移，故调整数组索引处理）
     *
     * @param basePointName 基点名称
     * @param nameInfo      实模名称解析结果
     * @return 生成的设备名称
     */
    private String generateDeviceName(String basePointName, InstanceNameInfo nameInfo) {
        // 基点名称按"_"分割为数组（新格式：[0:类型, 1:设备类型, 2:数字1, 3:数字2, 4:数字3]）
        String[] parts = basePointName.split("_");
        if (parts.length < 5) { // 新规则下至少需要5个部分（含设备类型）
            log.warn("基点名称格式异常（新规则要求至少5个部分）：{}", basePointName);
            return basePointName;
        }

        // 单行单列类型：编码不变
        if (nameInfo.isSingleRowOrColumn()) {
            return String.join("_", parts);
        }

        // 多行多列类型：替换第三个字段（原数字1的位置）为提取的数字
        String baseModelKey = nameInfo.getBaseModelKey();
        if (isSecondOrFourthLogic(baseModelKey)) {
            parts[2] = String.valueOf(nameInfo.getExtractedNumber()); // 数字字段顺次后移，原数字1的索引变为2
        }

        // 处理第六个字段（层数）
        String layerPart = nameInfo.isHasLayer() ? String.valueOf(nameInfo.getLayerNum()) : "null";
        return String.join("_", parts) + "_" + layerPart;
    }

    /**
     * 判断是否为第二或第四逻辑（多行多列类型）
     *
     * @param baseModelKey 基模关键字
     * @return 是否为多行多列类型
     */
    private boolean isSecondOrFourthLogic(String baseModelKey) {
        // 第二逻辑：全大屏过热器等
        Set<String> secondLogic = new HashSet<>(Arrays.asList("QDP", "HP", "ZZ", "GZ", "GG", "LSDG", "SMXXD"));
        // 第四逻辑：水平低温过热器等
        Set<String> fourthLogic = new HashSet<>(Arrays.asList("SPDG", "SMQ"));
        return secondLogic.contains(baseModelKey) || fourthLogic.contains(baseModelKey);
    }

    /**
     * 生成设备位置描述（将设备名称转换为可读性文字）
     * 适配新基点编码规则：数字字段索引+1
     *
     * @param deviceName 设备名称
     * @return 设备位置描述
     */
    private String generateDeviceDesc(String deviceName) {
        // 设备名称按"_"分割为数组（新格式：[0:类型, 1:设备类型, 2:数字1, 3:数字2, 4:数字3, 5:层数|null]）
        String[] parts = deviceName.split("_");
        if (parts.length < 5) { // 至少需要5个基础部分
            log.warn("设备名称格式异常，无法生成描述：{}", deviceName);
            return deviceName;
        }

        // 解析设备类型（如ZZ->中温再热器）
        String type = parts[0];
        String typeName;
        switch (type) {
            // 第一个逻辑：竖直排列单行管道
            case "HSX":
                typeName = "后水悬吊管";
                break;
            case "SLBQH":
                typeName = "炉膛水冷壁（前）";
                break;
            case "SLBLC":
                typeName = "炉膛水冷壁（侧）";
                break;
            case "SLBXP":
                typeName = "炉膛水冷壁（斜坡）";
                break;
            case "SLBLX":
                typeName = "水冷壁拉稀管";
                break;
            case "HSJBQH":
                typeName = "后竖井前后包墙";
                break;
            case "HSJBQLC":
                typeName = "后竖井侧包墙";
                break;
            case "SPYDBQLC":
                typeName = "水平烟道两侧包墙";
                break;
            case "BZQ":
                typeName = "壁式再热器（前）";
                break;
            case "BZLC":
                typeName = "壁式再热器（侧）";
                break;

            // 第二个逻辑：竖直排列多行管道
            case "QDP":
                typeName = "全大屏过热器";
                break;
            case "HP":
                typeName = "后屏过热器";
                break;
            case "ZZ":
                typeName = "中温再热器";
                break;
            case "GZ":
                typeName = "高温再热器";
                break;
            case "GG":
                typeName = "高温过热器";
                break;
            case "LSDG":
                typeName = "立式低温过热器";
                break;
            case "SMXXD":
                typeName = "省煤器悬吊管";
                break;

            // 第三个逻辑：水平排列单列管道
            case "DP":
                typeName = "顶棚过热器";
                break;
            case "SPYDBQS":
                typeName = "水平烟道上下包墙";
                break;
            case "HSJBQS":
                typeName = "后竖井顶包墙";
                break;

            // 第四个逻辑：水平排列多列管道
            case "SPDG":
                typeName = "水平低温过热器";
                break;
            case "SMQ":
                typeName = "省煤器";
                break;

            default:
                typeName = "未知设备类型";
        }

        // 构建描述信息（根据不同逻辑类型生成不同描述）
        StringBuilder desc = new StringBuilder(typeName);
        desc.append("，");

        if (isFirstOrThirdLogic(type)) {
            // 第一、三逻辑：单行单列（数字1的索引变为2）
            desc.append("从甲到乙第").append(parts[2]).append("管排，");
            desc.append(getPositionDescByType(type, parts[3])); // 数字2的索引变为3
        } else {
            // 第二、四逻辑：多行多列（数字1索引2，数字2索引3）
            desc.append(getMultiRowColDesc(type, parts[2], parts[3]));
        }

        // 添加层数信息（层数索引变为5）
        if (parts.length >= 6 && !"null".equals(parts[5])) {
            desc.append("，第").append(parts[5]).append("层");
        }

        return desc.toString();
    }

    /**
     * 判断是否为第一或第三逻辑（单行单列类型）
     *
     * @param baseModelKey 基模关键字
     * @return 是否为单行单列类型
     */
    private boolean isFirstOrThirdLogic(String baseModelKey) {
        // 第一逻辑：竖直排列单行
        Set<String> firstLogic = new HashSet<>(Arrays.asList("HSX", "SLBQH", "SLBLC", "SLBXP", "SLBLX",
                "HSJBQH", "HSJBQLC", "SPYDBQLC", "BZQ", "BZLC"));
        // 第三逻辑：水平排列单列
        Set<String> thirdLogic = new HashSet<>(Arrays.asList("DP", "SPYDBQS", "HSJBQS"));
        return firstLogic.contains(baseModelKey) || thirdLogic.contains(baseModelKey);
    }

    /**
     * 根据类型获取位置描述
     *
     * @param type     设备类型
     * @param position 位置编号（数字2，索引3）
     * @return 位置描述
     */
    private String getPositionDescByType(String type, String position) {
        // 第一逻辑（竖直排列）
        Set<String> firstLogic = new HashSet<>(Arrays.asList("HSX", "SLBQH", "SLBLC", "SLBXP", "SLBLX",
                "HSJBQH", "HSJBQLC", "SPYDBQLC", "BZQ", "BZLC"));
        if (firstLogic.contains(type)) {
            return "从上到下第" + position + "个测点";
        }
        // 第三逻辑（水平排列）
        return "从前到后第" + position + "个测点";
    }

    /**
     * 生成多行多列类型的位置描述
     *
     * @param type      设备类型
     * @param firstNum  第一个数字（索引2）
     * @param secondNum 第二个数字（索引3）
     * @return 位置描述
     */
    private String getMultiRowColDesc(String type, String firstNum, String secondNum) {
        // 第二逻辑（竖直排列多行）
        Set<String> secondLogic = new HashSet<>(Arrays.asList("QDP", "HP", "ZZ", "GZ", "GG", "LSDG", "SMXXD"));
        if (secondLogic.contains(type)) {
            return "从上到下第" + firstNum + "脚手架，从近到远第" + secondNum + "个测点";
        }
        // 第四逻辑（水平排列多列）
        return "从前到后第" + firstNum + "通道，从近到远第" + secondNum + "个测点";
    }

    /**
     * 生成实点数据
     * 实点数据逻辑不变，仅关联关系依赖设备ID
     *
     * @param instanceModel 实模对象
     * @param basePoint     基点对象
     * @param device        设备对象
     * @return 实点对象
     */
    private ModelInstancePoints generateInstancePoint(ModelInstanceModels instanceModel,
                                                      ModelBasePoints basePoint,
                                                      DevDevices device) {
        ModelInstancePoints instancePoint = new ModelInstancePoints();
        instancePoint.setInstanceId(instanceModel.getInstanceId());
        instancePoint.setPointId(basePoint.getPointId());
        instancePoint.setDeviceId(device.getDeviceId());
        // 可根据需要设置其他属性（如坐标偏移量、时间等）
        return instancePoint;
    }

    /**
     * 实模名称解析结果封装类
     */
    @Data
    private static class InstanceNameInfo {
        // 基模名称
        private String baseModelName;
        // 是否有层结构
        private boolean hasLayer;
        // 层数
        private Integer layerNum;
        // 提取的数字（管排编号等）
        private Integer extractedNumber;
        // 是否为单行单列类型
        private boolean isSingleRowOrColumn;
        // 基模关键字（用于判断逻辑类型）
        private String baseModelKey;
    }
}